//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Workflow.ComponentModel.Design;
using System.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using Microsoft.Research.ScientificWorkflow;
using System.IO;
using System.Reflection;

namespace TridentBasicActivities.Activities
{
    [Name("For Loop Activitiy for looping through child activities", "For Loop")]
    [Description("Iterates over child activities depending on the values of Start, Stop and Step.")]
    [WorkflowCategory("Control Flow")]
    [ActivityValidator(typeof(ValidateForActivity))]
    public class ForActivity : System.Workflow.Activities.SequenceActivity,
        IActivityEventListener<ActivityExecutionStatusChangedEventArgs>
    {
        public ForActivity()
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);                
        }

        public static System.Workflow.ComponentModel.DependencyProperty StartProperty =
            System.Workflow.ComponentModel.DependencyProperty.Register("Start", typeof(int),
            typeof(ForActivity));

        [RequiredInputParam]
        [Name("Start Value")]
        [Description("Start value of the counter for For Loop.")]
        public int Start
        {
            get { return (int)GetValue(ForActivity.StartProperty); }
            set { SetValue(ForActivity.StartProperty, value); }
        }

        public static System.Workflow.ComponentModel.DependencyProperty EndProperty =
            System.Workflow.ComponentModel.DependencyProperty.Register("End", typeof(int),
            typeof(ForActivity));

        [RequiredInputParam]
        [Name("End Value")]
        [Description("End value for determining when to stop executing the FOR loop.")]
        public int End
        {
            get { return (int)GetValue(ForActivity.EndProperty); }
            set { SetValue(ForActivity.EndProperty, value); }
        }

        public static System.Workflow.ComponentModel.DependencyProperty StepProperty =
            System.Workflow.ComponentModel.DependencyProperty.Register("Step", typeof(int),
            typeof(ForActivity));

        [RequiredInputParam]
        [Name("Step Value")]
        [Description("Step value for incrementing the counter each time one interation is executed.")]
        public int Step
        {
            get { return (int)GetValue(ForActivity.StepProperty); }
            set { SetValue(ForActivity.StepProperty, value); }
        }

        public static System.Workflow.ComponentModel.DependencyProperty CurrentIndexProperty =
            System.Workflow.ComponentModel.DependencyProperty.Register("CurrentIndex", typeof(int),
            typeof(ForActivity), new PropertyMetadata(-1));

        [OutputParam]
        [Name("Current Index")]
        [Description("The current iteration Index value.")]
        public int CurrentIndex
        {
            get { return (int)GetValue(ForActivity.CurrentIndexProperty); }
            set { SetValue(ForActivity.CurrentIndexProperty, value); }
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            this.CurrentIndex = this.Start;

            if (ValidateParameters() && Continue())
            {
                ExecuteIteration(executionContext);
                return ActivityExecutionStatus.Executing;
            }
            AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;                
            return ActivityExecutionStatus.Closed;
        }

        private void ExecuteIteration(ActivityExecutionContext parentCtx)
        {
            this.TrackData("IterationValue",this.CurrentIndex);

            System.Workflow.ComponentModel.Activity act = base.EnabledActivities[0];
                
            ActivityExecutionContext newContext = parentCtx.ExecutionContextManager.CreateExecutionContext(act);

            newContext.Activity.RegisterForStatusChange(System.Workflow.ComponentModel.Activity.ClosedEvent, this);
           
            newContext.ExecuteActivity(newContext.Activity);
        }

        public void OnEvent(object sender, ActivityExecutionStatusChangedEventArgs e)
        {
            //get the AEC for the For activity
            ActivityExecutionContext ctx = sender as ActivityExecutionContext;
            if (ctx == null)
                throw new ArgumentNullException("sender", "The sender must be an AEC");

            //unregister for the events and close the context
            e.Activity.UnregisterForStatusChange(
                System.Workflow.ComponentModel.Activity.ClosedEvent, this);

            //get the child context and mark it as complete
            ActivityExecutionContext childContext = ctx.ExecutionContextManager.GetExecutionContext(e.Activity);
            ctx.ExecutionContextManager.CompleteExecutionContext(childContext);

            // Increment the counter.
            this.CurrentIndex += this.Step;

            //determine if we should close or execute another iteration
            if (Continue())
            {
                ExecuteIteration(ctx);
            }
            else
            {
                AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;                
                ctx.CloseActivity();
            }
        }

        /// <summary>
        /// Initial validation of parameters.
        /// </summary>
        /// <returns>True if parameters have valid values.</returns>
        private bool ValidateParameters()
        {
            if (this.Step > 0)
            {
                if (this.Start <= this.End)
                {
                    return true;
                }
            }
            else if (this.Step < 0)
            {
                if (this.Start >= this.End)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Check if the counter is within the stop value.
        /// </summary>
        /// <returns>true if the counter is less than or equal to the end value.</returns>
        private bool Continue()
        {
            if (this.Step > 0)
            {
                if (this.CurrentIndex <= this.End)
                {
                    return true;
                }
            }
            else
            {
                if (this.CurrentIndex >= this.End)
                {
                    return true;
                }
            }
            return false;
        }

        private Assembly ResolveAssembly(object sender, ResolveEventArgs args)
        {
            Assembly resolvedAssembly = null;

            //  If it is a type you know about return the assembly
            if (args.Name.Equals(this.GetType().Assembly.FullName))
            {
                resolvedAssembly = this.GetType().Assembly;
            }

            return resolvedAssembly;
        }

    }
}
